package bch.rocketmq.util;

public class Snowflake {
    // 起始的时间戳，可以设置为项目开始运行的时间
    private final long twepoch = 1609459200000L; // 2021-01-01 00:00:00 GMT+00:00
    private final long datacenterIdBits = 5L;
    private final long machineIdBits = 5L;
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    private final long maxMachineId = -1L ^ (-1L << machineIdBits);
    private final long sequenceBits = 12L;
    private final long datacenterIdShift = sequenceBits + machineIdBits;
    private final long machineIdShift = sequenceBits;
    private final long timestampLeftShift = sequenceBits + machineIdBits + datacenterIdBits;
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    private long datacenterId;
    private long machineId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;

    public Snowflake(long datacenterId, long machineId) {
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException("Datacenter ID can't be greater than " + maxDatacenterId + " or less than 0");
        }
        if (machineId > maxMachineId || machineId < 0) {
            throw new IllegalArgumentException("Machine ID can't be greater than " + maxMachineId + " or less than 0");
        }
        this.datacenterId = datacenterId;
        this.machineId = machineId;
    }

    public synchronized long nextId() {
        long timestamp = timeGen();
        if (timestamp < lastTimestamp) {
            return futureId();
        }
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        return ((timestamp - twepoch) << timestampLeftShift) |
                (datacenterId << datacenterIdShift) |
                (machineId << machineIdShift) |
                sequence;
    }

    private long futureId() {
        sequence = (sequence + 1) & sequenceMask;
        if (sequence == 0L) {
            lastTimestamp = lastTimestamp + 1;
        }

        return (lastTimestamp - twepoch) << timestampLeftShift //时间戳部分
                | datacenterId << datacenterIdShift       //数据中心部分
                | machineId << machineIdShift             //机器标识部分
                | sequence;                             //序列号部分
    }

    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }

    public static void main(String[] args) {
        Snowflake snowflake = new Snowflake(1, 1);

        // 生成10个ID进行演示
        for (int i = 0; i < 10; i++) {
            long id = snowflake.nextId();
            System.out.println("Generated ID: " + id);
        }
    }
}
